package stone926.mods.more_enchantments.mixins;

import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.entity.projectile.ProjectileEntity;
import net.minecraft.item.CrossbowItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import stone926.mods.more_enchantments.MoreEnchantmentsMod;
import stone926.mods.more_enchantments.enchantments.legendary.StrafingEnchantment;
import stone926.mods.more_enchantments.interfaces.IPersistentProjectileEntity;
import stone926.mods.more_enchantments.util.EnchantmentUtil;

import java.util.List;
import java.util.Random;
import java.util.function.Consumer;

@Mixin(CrossbowItem.class)
public abstract class CrossbowItemMixin extends Item {

  public CrossbowItemMixin(Settings settings) {
    super(settings);
  }

  @Shadow
  private static List<ItemStack> getProjectiles(ItemStack crossbow) {
    return null;
  }

  @Shadow
  private static void shoot(World world, LivingEntity shooter, Hand hand, ItemStack crossbow, ItemStack projectile, float soundPitch, boolean creative, float speed, float divergence, float simulated) { }

  @Shadow
  private static float getSoundPitch(boolean flag, Random random) {
    return 0;
  }

  @Shadow
  private static void postShoot(World world, LivingEntity entity, ItemStack stack) { }

  @Redirect(method = "loadProjectile", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;split(I)Lnet/minecraft/item/ItemStack;"))
  private static ItemStack handleInfinite(ItemStack arrowStack, int amount, LivingEntity shooter, ItemStack crossbow, ItemStack projectile, boolean simulated, boolean creative) {
    if (EnchantmentHelper.getLevel(MoreEnchantmentsMod.INFINITE, crossbow) > 1) {
      ItemStack itemStack = arrowStack.copy();
      itemStack.setCount(amount);
      return itemStack;
    }
    return arrowStack.split(amount);
  }

  @Redirect(method = "shoot", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/projectile/ProjectileEntity;setVelocity(DDDFF)V"))
  private static void handleFlash(
    ProjectileEntity projectile, double x, double y, double z, float speed, float divergence,
    World world, LivingEntity shooter, Hand hand, ItemStack crossbow
  ) {
    int flashLvl = EnchantmentHelper.getLevel(MoreEnchantmentsMod.FLASH, crossbow);
    if (flashLvl > 0) {
      projectile.setVelocity(x, y, z, speed * (flashLvl * 0.5F + 0.7F), divergence / (flashLvl + 1));
      if (projectile instanceof PersistentProjectileEntity persistentProjectile) {
        persistentProjectile.setDamage(persistentProjectile.getDamage() + flashLvl);
      }
    } else projectile.setVelocity(x, y, z, speed, divergence);
  }

  @Inject(method = "shoot", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/item/CrossbowItem;createArrow(Lnet/minecraft/world/World;Lnet/minecraft/entity/LivingEntity;Lnet/minecraft/item/ItemStack;Lnet/minecraft/item/ItemStack;)Lnet/minecraft/entity/projectile/PersistentProjectileEntity;"), locals = LocalCapture.CAPTURE_FAILSOFT)
  private static void setPickUpType(
    World world, LivingEntity shooter, Hand hand, ItemStack crossbow, ItemStack projectile,
    float soundPitch, boolean creative, float speed, float divergence, float simulated, CallbackInfo ci,
    boolean b, ProjectileEntity projectileEntity
  ) {
    if (projectileEntity instanceof PersistentProjectileEntity persistentProjectileEntity) {
      ItemStack stack = shooter.getStackInHand(hand);
      if (EnchantmentUtil.hasEnchantment(MoreEnchantmentsMod.INFINITE, stack) || EnchantmentUtil.hasEnchantment(MoreEnchantmentsMod.STRAFING, stack)) {
        persistentProjectileEntity.pickupType = PersistentProjectileEntity.PickupPermission.CREATIVE_ONLY;
      }
      if (EnchantmentUtil.hasEnchantment(MoreEnchantmentsMod.STRAFING, stack)) {
        ((IPersistentProjectileEntity) persistentProjectileEntity).setDisappearCountdown(30);
      }
    }
  }

  @Redirect(method = "shoot", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;damage(ILnet/minecraft/entity/LivingEntity;Ljava/util/function/Consumer;)V"))
  private static void preventStrafingDamage(ItemStack stack, int amount, LivingEntity shooter, Consumer<LivingEntity> breakCallback) {
    if (EnchantmentUtil.hasEnchantment(MoreEnchantmentsMod.STRAFING, stack) && shooter.getRandom().nextBoolean()) {
      stack.damage(1, shooter, breakCallback);
    } else stack.damage(amount, shooter, breakCallback);
  }

  @Inject(method = "shootAll", at = @At("HEAD"), cancellable = true)
  private static void strafing(World world, LivingEntity entity, Hand hand, ItemStack stack, float speed, float divergence, CallbackInfo ci) {
    int strafingLvl = EnchantmentHelper.getLevel(MoreEnchantmentsMod.STRAFING, stack);
    if (strafingLvl > 0) {
      int shootCnt = StrafingEnchantment.getShootCount(strafingLvl);
      float range = StrafingEnchantment.getRange(strafingLvl);
      boolean bl = entity instanceof PlayerEntity && ((PlayerEntity) entity).getAbilities().creativeMode;
      ItemStack projectileStack = getProjectiles(stack).get(0);
      for (int i = 0; i < shootCnt; i++) {
        shoot(world, entity, hand, stack, projectileStack, getSoundPitch(true, entity.getRandom()), bl, speed, divergence, -range + range * 2 * i / (shootCnt + 1));
      }
      postShoot(world, entity, stack);
      ci.cancel();
    }
  }

}
